#include <unistd.h>
#include "../src/SSP_College.hpp"
#include "../src/confReader.hpp"
#include "../src/h264decode_gst.hpp"
#include "../src/h264Save_gst.hpp"
#include "../src/ShareMem.hpp"
#include "../src/POSIX_MsgServer.hpp"
#include "../src/FrameCounter.hpp"
#include "../src/StringRegex.hpp"
#include <yaml-cpp/yaml.h>
#include <semaphore.h>
#include <thread>
#include <pthread.h>
#include <cstring>
#include <list>
#include <httplib.h>
#include <nlohmann/json.hpp>
#include <fstream>

const char* configFilePath="/tmp/HBTY_server_ipc.yaml";
const char* MsgServerName="/HBTY_MSGSERVER_CAMERACOLLEGE";

class CameraObject
{
  private:
    H264Save* saver=nullptr;
    h264decode_gst* decoder=nullptr;
    FrameCounter* counter=nullptr;
    ShareMem shareMem;
    std::thread workThread;
    pthread_t workThreadID;
    std::string CameraUUID;

  public:
    CameraObject(std::string id,const char* shareMemName,std::size_t shareMemSize)
      : shareMem(shareMemName,shareMemSize)
      , CameraUUID(id)
    {
    }

    virtual ~CameraObject()
    {
      if(workThread.joinable())
      {
        pthread_cancel(workThreadID);
        workThread.join();
      }
      if(saver!=nullptr)
        delete saver;
      if(counter!=nullptr)
        delete counter;
      delete decoder;
    }

    static void HandleData(const char* data,std::size_t size,unsigned long frameID,std::any & userData)
    {
      auto item_ptr=std::any_cast<std::list<CameraObject>::iterator>(userData);
      if(item_ptr->decoder!=nullptr)
        item_ptr->decoder->InputNewImage((unsigned char*)data,size,frameID);
      if(item_ptr->saver!=nullptr)
        item_ptr->saver->InputH264(data,size);
      if(item_ptr->counter!=nullptr)
        item_ptr->counter->InputFrameID(frameID);
    }

    void Handle_NewImageCallback(ImageType& data)
    {
      strcpy(data.uuid,CameraUUID.c_str());
      memcpy(shareMem.GetMemory(),&data,sizeof(ImageType));
      memcpy((char*)shareMem.GetMemory()+sizeof(ImageType),data.data,data.data_size);
    }

    void StartPreview()
    {
      if(decoder!=nullptr)
        throw std::runtime_error("正在预览中，不能重复启动");
      decoder=new h264decode_gst();
      decoder->funcHandleData=std::bind(&CameraObject::Handle_NewImageCallback,this,std::placeholders::_1);
      // workThread=std::thread([this](){
      //   workThreadID=pthread_self();
      //   while(true)
      //   {
      //     ImageType data=decoder->GetNewImage();
      //     strcpy(data.uuid,CameraUUID.c_str());
      //     memcpy(shareMem.GetMemory(),&data,sizeof(ImageType));
      //     memcpy((char*)shareMem.GetMemory()+sizeof(ImageType),data.data,data.data_size);
      //     delete [] data.data;
      //     data.data=nullptr;
      //   }
      // });
    }

    void StopPreview()
    {
      if(workThread.joinable())
      {
        pthread_cancel(workThreadID);
        workThread.join();
      }
      h264decode_gst* decode_ptr=decoder;
      decoder=nullptr;
      delete decode_ptr;
    }

    void StartSaveData(const std::string & filePath)
    {
      if(saver!=nullptr)
        throw std::runtime_error("正在录制文件中，不能重复启动");
      counter=new FrameCounter([this,&filePath]()->std::string{
        std::stringstream res;
        if(*(filePath.rbegin())!='/')
          res<<filePath<<'/';
        else
          res<<filePath;
        res<<CameraUUID<<".frame";
        return res.str();
      }());
      saver=new H264Save([this,&filePath]()->std::string{
        std::stringstream res;
        if(*(filePath.rbegin())!='/')
          res<<filePath<<'/';
        else
          res<<filePath;
        res<<CameraUUID<<".mkv";
        return res.str();
      }());
    }

    void StopSaveData()
    {
      //这里有异步调用的问题在
      H264Save* saver_ptr=saver;
      FrameCounter* counter_ptr=counter;
      saver=nullptr;
      counter=nullptr;
      delete saver_ptr;
      delete counter_ptr;
    }

    bool GetSaveData()
    {
      return saver!=nullptr;
    }
    
    bool GetMonitor()
    {
      return decoder!=nullptr;
    }
};

std::list<CameraObject> CameraObjectList;
SSPCollege* cameraCollege_main;
POSIX_MsgServer* msgServer;
StringRegex* strDecode;

void Handle_SIGINT(int)
{

}

void Handle_MsgReceive(std::string && recData)
{
  if(strDecode->HandleString(recData))
    std::cout<<"New Command "<<recData<<std::endl;
  else
    std::cout<<"Can't Decode Receive Data "<<recData<<std::endl;
}

int main()
{
  gst_init(nullptr,nullptr);
  //计算基础数据
  long ShareMEMSize=sizeof(ImageType)+1920*1080*3;
  std::cout<<"System Boot ... "<<std::endl;
  //准备引擎
  {
    std::ofstream outputConfig(configFilePath,std::ios::trunc);
    if(!outputConfig.is_open())
      throw std::runtime_error("无法写入配置文件");
    confReader conf;
    std::vector<std::string> cameraList=conf.GetCameraList();
    std::vector<std::pair<std::string,std::any>> initColleges;
    YAML::Node config;
    config["shm_size"]=ShareMEMSize;
    config["num"]=cameraList.size();
    initColleges.reserve(cameraList.size());
    int shareCounter=0;

    for(const auto & i : cameraList)
    {
      //生成共享内存名称
      std::string shareMemName=(std::stringstream()<<"/HBTY_SHAREMEM_ORIMAGE"<<shareCounter++).str();
      //获取相机ID
      httplib::Client client(((std::stringstream())<<"http://"<<i).str());
      auto res=client.Get("/info");
      if(res==nullptr)
        throw std::runtime_error("Can't send HTTP request");
      else if(res->status!=200)
        throw std::runtime_error((std::stringstream()<<"The request is error code : "<<res->status).str());
      nlohmann::json jsonData=nlohmann::json::parse(res->body);
      if(!jsonData["sn"].is_string())
        throw std::runtime_error("无法解析相机ID");
      CameraObjectList.emplace_back(
        (std::stringstream()<<"C1"<<jsonData["sn"].get<std::string>()).str(),
        shareMemName.c_str(),
        ShareMEMSize
      );
      initColleges.emplace_back(i,std::prev(CameraObjectList.end()));
      config["shm"].push_back(shareMemName);
    }
    cameraCollege_main=new SSPCollege(initColleges,CameraObject::HandleData);
    msgServer=new POSIX_MsgServer(MsgServerName);
    outputConfig<<config;
    outputConfig.close();
  }
  //准备正则解析引擎
  strDecode=new StringRegex;
  strDecode->append(R"(Record Start ([\w\/]+))",[](std::smatch res){
    for(auto & i : CameraObjectList)
      i.StartSaveData(res[1]);
  });
  strDecode->append(R"(Record Stop)",[](std::smatch){
    for(auto & i : CameraObjectList)
      i.StopSaveData();
  });
  strDecode->append(R"(Monitor Start)",[](std::smatch){
    for(auto & i : CameraObjectList)
        i.StartPreview();
    cameraCollege_main->StartShot();
  });
  strDecode->append(R"(Monitor Stop)",[](std::smatch){
    cameraCollege_main->StopShot();
    for(auto & i : CameraObjectList)
        i.StopPreview();
  });
  strDecode->append(R"(Status Record)",[](std::smatch){
    for(auto & i : CameraObjectList)
    {
      if(i.GetSaveData())
        msgServer->SendData("True");
      else
        msgServer->SendData("False");
      return;
    }
  });
  strDecode->append(R"(Status Monitor)",[](std::smatch){
    for(auto & i : CameraObjectList)
    {
      if(i.GetMonitor())
        msgServer->SendData("True");
      else
        msgServer->SendData("False");
      return;
    }
  });
  std::cout<<"Sytem Ready ..."<<std::endl;

  msgServer->BootReceive(Handle_MsgReceive);
  signal(SIGINT,Handle_SIGINT);

  while(sleep(10)==0)
    ;

  delete msgServer;
  delete cameraCollege_main;
  CameraObjectList.clear();
}
